#include <jasper/jasper.h>
#include <tiffio.h>
#include <iostream>

#define HAVE_LIBTIFF 1
#include "tifflib.h"
#include "warpsol.h"


/**********************************************************************************************/
/* The implementation of the compressor / uncompressor                                        */
/**********************************************************************************************/

const int warpSol::SOLEXA_IMG_PRECISION=12;


int warpSol::writeSolexa(string fnam, int plane) {
  const char *description = "Do we need one?";
  const char *f = fnam.c_str();
  const int bytesperpixel = 2;

  assert(plane < _images.size()); 


  int nbytes;
  TIFF *tif = TIFFOpen(f, "w");
    if (tif == NULL) {
      std::cerr << "Error: Failed to open tiff file " << f << " for output" << std::endl;
        return 0;
    }

    TIFFSetField(tif, TIFFTAG_IMAGEWIDTH, (uint16) _nx);
    TIFFSetField(tif, TIFFTAG_IMAGELENGTH, (uint16) _ny);
    TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE, (uint16) (8 * bytesperpixel));
    TIFFSetField(tif, TIFFTAG_COMPRESSION, COMPRESSION_NONE);
    TIFFSetField(tif, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_MINISBLACK);
    TIFFSetField(tif, TIFFTAG_IMAGEDESCRIPTION, description);
    TIFFSetField(tif, TIFFTAG_XRESOLUTION, 1.);
    TIFFSetField(tif, TIFFTAG_YRESOLUTION, 1.);
    TIFFSetField(tif, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG);
    TIFFSetField(tif, TIFFTAG_RESOLUTIONUNIT, RESUNIT_NONE);
    TIFFSetField(tif, TIFFTAG_SOFTWARE, "Modified Solexa TIFF-write ");
    {
        time_t t = time(NULL);
        struct tm *ts = localtime(&t);
        char date[20];
        if (snprintf(date, sizeof(date), "%4d:%02d:%02d %02d:%02d:%02d",
                     ts->tm_year + 1900, ts->tm_mon + 1, ts->tm_mday,
                     ts->tm_hour, ts->tm_min, ts->tm_sec) > 20)
            date[19] = '\0';
        TIFFSetField(tif, TIFFTAG_DATETIME, date);
    }

    nbytes = TIFFWriteRawStrip(tif, 0, _images[plane], _nx * _ny * bytesperpixel);
    if (nbytes <= 0)
      std::cerr << "Could not write to tiff file " <<  f << std::endl;

    TIFFClose(tif);
    return 0;
}



int warpSol::readSolexa(string fnam) {
  int nx, ny, ns;
  const char *f = fnam.c_str();
  struct tiff_file *in = fopen_tiff(f, "r", 1);
  if(!in) {
    std::cerr << "Can not open Solexa Image " << fnam << std::endl;
    return  -1;
  }

  fget_tiff_size(in, &nx, &ny, &ns);
  if(nPlane()) assert(_nx == nx && _ny == ny && _ns == ns);
  else                 _nx = nx,   _ny  = ny,   _ns  = ns;        


  int need_mem = sizeof(unsigned short int) * _nx * _ny * _ns;
  unsigned short int *image = new unsigned short int [need_mem];
  if(fget_tiff_data(in, image, _nx * _ny , 0)) {
    std::cerr << "Can not read Solexa Image " << fnam << std::endl;
    delete [] image;
    return -1;
  }
  fclose_tiff(in);
  _images.push_back(image);
  return 0;
}



int warpSol::readJasper(string fnam, const char *format) {
  const char *f = fnam.c_str();
  char buffer[256];
  strcpy(buffer, format);
  int fmt = jas_image_strtofmt(buffer);

  if(fmt < 0) {
    std::cerr << "Output format '" << format << "' not found for " << fnam << std::endl;
    return -1;
  }

  jas_stream_t *fd;
  if(! (fd = jas_stream_fopen(f, "r+b"))) { 
    std::cerr << "Can not open outfile " << fnam;
    return -1;
  }

  jas_image_t *image = jas_image_decode(fd, fmt, 0);
  jas_stream_close(fd);

  if(!image) {
    std::cerr << "Can not read jasper input " << fnam << std::endl;
    return -1;
  }
	       
  clear();
  const unsigned int P = jas_image_numcmpts(image); 

  _nx = jas_image_width(image);
  _ny = jas_image_height(image);
  const int need_mem = sizeof(unsigned short int) * _nx * _ny;
  
  if(!P) {
    std::cerr << "Image " << fnam << " does not contain data" << std::endl;
    jas_image_destroy(image);
    return -1;
  }


  jas_matrix_t *data = jas_matrix_create(_ny, _nx); 
  for(unsigned int p=0; p < P; ++p) {
    jas_image_readcmpt(image, p, 0, 0, _nx, _ny, data);
    const unsigned int depth = jas_image_cmptprec(image, p);
    if( jas_image_cmptheight(image,p) != _ny ||
        jas_image_cmptwidth(image,p)  != _nx ||
        jas_image_cmpttlx(image,p)    != 0   ||
        jas_image_cmpttly(image,p)    != 0  ) {
      std::cerr << "Image geometry not supported during readJasper" << std::endl;
      jas_matrix_destroy(data);
      jas_image_destroy(image);
      return -1;
    }
    unsigned short int *memimage = new unsigned short int [need_mem];

    for(unsigned int y = 0; y < _ny; ++y) {
      for(unsigned int x = 0; x < _nx; ++x) {
	unsigned short int v = jas_matrix_get(data, y, x);
        memimage[y * _nx + x] = v; 
      }
    }
    _images.push_back(memimage);
  }
  jas_matrix_destroy(data);
  jas_image_destroy(image);
  return 0;
} 


int warpSol::writeJasper(string fnam, const char *options, const char *format) {
  char buffer[256];
  const unsigned int P = nPlane();
  const char *f = fnam.c_str();


  strcpy(buffer, format);
  int fmt = jas_image_strtofmt(buffer);
  if(fmt < 0) {
    std::cerr << "Output format '" << format << "' not found?" << fnam;
    return -1;
  }

  jas_stream_t *fd;
  if(! (fd = jas_stream_fopen(f, "w+b"))) {
    std::cerr << "Can not open outfile " << fnam;
    return -1;
  }


  // Setup Jasper Image component parameters
  jas_image_cmptparm_t *cmpt = new jas_image_cmptparm_t [P];
  for(unsigned int i=0; i < P; ++i) {
    cmpt[i].tlx    = 0;
    cmpt[i].tly    = 0;
    cmpt[i].width  = _nx;
    cmpt[i].height = _ny;
    cmpt[i].hstep  = 1;
    cmpt[i].vstep  = 1;
    cmpt[i].prec   = SOLEXA_IMG_PRECISION;
    cmpt[i].sgnd   = 0;
  } 

  // create Jasper Image
  jas_image_t *image = jas_image_create(P, cmpt, JAS_CLRSPC_SGRAY);
  jas_image_setclrspc(image, JAS_CLRSPC_GENGRAY);

  // Create Japser presentation of the layered image
  jas_matrix_t *data = jas_matrix_create(_ny, _nx); 

  for(unsigned int p=0; p < P; ++p) {
    jas_image_setcmpttype(image, p, JAS_CLRSPC_CHANIND_GRAY_Y);
    for(unsigned int y = 0; y < _ny; ++y) {
     for(unsigned int x = 0; x < _nx; ++x) {
	unsigned short int v = _images[p][y * _nx + x]; // CHECK ME!!!!!
	 jas_matrix_set(data, y, x, v); 
      }
    } 
    jas_image_writecmpt(image, p, 0, 0, _nx, _ny, data);
  }

  strcpy(buffer, options);
  jas_image_encode(image, fd, fmt, buffer);
  jas_matrix_destroy(data);
  jas_stream_close(fd);
  jas_image_destroy(image);
  delete [] cmpt;
  return 0;
}


void warpSol::createImage(int nx, int ny, int ns) {
  clear();
  for(int i=0; i < ns; ++i) {
    unsigned short int *im = new unsigned short int[nx * ny];
    _images.push_back(im);
  }
  _nx = nx;
  _ny = ny;
  _ns = ns;
  return;
}
